// src/main/java/com/dbms/execution/execution/NestedLoopJoinExecutor.java
package com.dbms.execution.execution;

import com.dbms.common.Schema;
import com.dbms.common.Tuple;
import com.dbms.execution.plan.PlanNode;

import java.util.ArrayList;
import java.util.List;

/**
 * NestedLoopJoinExecutor (嵌套循环连接执行器) 实现了最基础的嵌套循环连接算法。
 * 它的工作原理是：
 * 1. 从外表 (outerExecutor) 取出一条元组。
 * 2. 遍历内表 (innerExecutor) 的所有元组。
 * 3. 对每一对内外表元组，检查它们是否满足连接条件 (joinPredicate)。
 * 4. 如果满足，则将它们合并成一个结果元组并返回。
 * 5. 重复步骤 2-4 直到内表遍历完毕。
 * 6. 重复步骤 1-5 直到外表遍历完毕。
 */
public class NestedLoopJoinExecutor implements Executor {

    /**
     * 外表（或左表）的执行器。
     */
    private final Executor outerExecutor;
    /**
     * 内表（或右表）的执行器。
     */
    private final Executor innerExecutor;
    /**
     * 连接谓词，用于判断两个元组是否应该被连接。
     */
    private final Predicate joinPredicate;
    /**
     * 连接操作的输出模式。
     */
    private final Schema outputSchema;
    /**
     * 保存当前从外表中取出的元组，用于与内表中的所有元组进行比较。
     */
    private Tuple outerTuple = null;

    public NestedLoopJoinExecutor(Executor outerExecutor, Executor innerExecutor, Predicate predicate, Schema outputSchema) {
        this.outerExecutor = outerExecutor;
        this.innerExecutor = innerExecutor;
        this.joinPredicate = predicate;
        this.outputSchema = outputSchema;
    }

    @Override
    public void open() {
        outerExecutor.open();
        innerExecutor.open();
        this.outerTuple = null;
    }

    @Override
    public Tuple next() {
        // 使用一个无限循环，直到找到一个匹配的元组或所有数据都已处理完毕
        while (true) {
            // 如果当前的 outerTuple 为 null (意味着我们需要从外表获取一个新元组)
            if (outerTuple == null) {
                outerTuple = outerExecutor.next();
                // 如果外表已经没有数据了，则整个连接过程结束
                if (outerTuple == null) {
                    return null;
                }
                // 【关键】每当获取一个新的外表元组，都必须重置内表执行器，使其从头开始扫描。
                innerExecutor.close();
                innerExecutor.open();
            }

            // 使用当前的 outerTuple 遍历内表
            while (true) {
                Tuple innerTuple = innerExecutor.next();
                // 如果内表已经遍历完了
                if (innerTuple == null) {
                    // 将 outerTuple 设为 null，这样外层循环就会去获取下一个外表元组
                    outerTuple = null;
                    break; // 跳出内层循环
                }

                // 检查连接条件是否满足
                if (joinPredicate.evaluate(outerTuple, innerTuple)) {
                    // 如果满足条件，将两个元组的值拼接成一个新的结果元组
                    List<Object> combinedValues = new ArrayList<>();
                    // 添加外表元组的所有值
                    for (int i = 0; i < outerExecutor.getSchema().getColumnCount(); i++) {
                        combinedValues.add(outerTuple.getValue(i));
                    }
                    // 添加内表元组的所有值
                    for (int i = 0; i < innerExecutor.getSchema().getColumnCount(); i++) {
                        combinedValues.add(innerTuple.getValue(i));
                    }
                    // 返回拼接后的新元组
                    return new Tuple(combinedValues, outputSchema);
                }
            }
        }
    }

    @Override
    public void close() {
        outerExecutor.close();
        innerExecutor.close();
    }

    @Override
    public Schema getSchema() {
        return outputSchema;
    }

    @Override
    public PlanNode getPlanNode() {
        return null;
    }
}